from machine import Pin, Timer, SoftI2C
import utime as time
from ssd1306 import SSD1306_I2C as OledDisplay
import urandom as random

# 游戏参数
OLED_WIDTH = 128
OLED_HEIGHT = 64
GRID_SIZE = 6
DIRECT_UP = (0, -1)
DIRECT_DOWN = (0, 1)
DIRECT_LEFT = (-1, 0)
DIRECT_RIGHT = (1, 0)
SNAKE_RECT = 0
SNAKE_CIRCLE = 1
INITIAL_SNAKE_POS = [(28, 22), (22, 22), (16, 22)]
INITIAL_SNAKE_DIRECT = DIRECT_RIGHT
INITIAL_SPEED = 360

# 引脚
KEY_PINS = [14, 27, 26, 25]
I2C_SCL = 18
I2C_SDA = 23

# 创建I2C对象
i2c = SoftI2C(scl=Pin(I2C_SCL), sda=Pin(I2C_SDA))
# 创建按键对象
keys = [Pin(pin, Pin.IN, Pin.PULL_UP) for pin in KEY_PINS]
# 初始化定时器
timer_key = Timer(0)
timer_game = Timer(1)


# snake
class Snake():
    def __init__(
        self,
        pos=INITIAL_SNAKE_POS.copy(),
        direct=DIRECT_RIGHT,
        size=GRID_SIZE,
        shape=SNAKE_CIRCLE
    ):
        # 蛇身每个点的位置
        self.pos = pos
        # 移动方向
        self.direct = direct
        # 大小
        self.size = size
        # 形状
        self.shape = shape
        
    def __len__(self):
        return len(self.pos)
        

class SnakeGame(OledDisplay):
    def __init__(self):
        # 初始化OLED显示屏
        super().__init__(OLED_WIDTH, OLED_HEIGHT, i2c)
        # 创建snake类
        self.snake = Snake()
        # 游戏状态： 0开始 1进行 2结束
        self.status = 0
        self.key_down = [0, 0]
        self.key_old = 0
        self.init_game()
        
    def init_game(self):
        """ 初始化游戏 """
        self.snake.pos = INITIAL_SNAKE_POS.copy()
        self.snake.direct = DIRECT_RIGHT
        self.speed = INITIAL_SPEED
        self.food = None
        self.generate_food()
        self.score = 0
        self.init_ui()
        self.update_ui()
        timer_key.init(period=20, mode=Timer.PERIODIC, callback=self.keys_handler)
        timer_game.init(period=INITIAL_SPEED, mode=Timer.PERIODIC, callback=self.game_loop)
    
    def game_loop(self, timer):
        """ 游戏循环 """
        if self.status == 1:
            # 蛇移动
            self.move_handler()
            # 游戏结束检测
            self.check_over()
            # 更新游戏界面
            self.update_ui()
        
    def keys_handler(self, timer):
        """ 按键处理子函数 """
        key_code = 0
        # 扫描4个按键
        for idx, key in enumerate(keys):
            if key.value() == 0:
                key_code = idx + 1
        self.key_down[0] = key_code & (key_code ^ self.key_old)
        if self.key_down[0] != 0:
            self.key_down[1] = self.key_down[0]
        self.key_old = key_code
        
        if self.status == 0:
            # 开始游戏
            if self.key_down[1] == 4:
                self.status = 1
        elif self.status == 1:
            # 游戏中，控制方向
            if self.key_down[1] == 2 and self.snake.direct != DIRECT_DOWN:
                self.snake.direct = DIRECT_UP
            elif self.key_down[1] == 3 and self.snake.direct != DIRECT_UP:
                self.snake.direct = DIRECT_DOWN
            elif self.key_down[1] == 1 and self.snake.direct != DIRECT_RIGHT:
                self.snake.direct = DIRECT_LEFT
            elif self.key_down[1] == 4 and self.snake.direct != DIRECT_LEFT:
                self.snake.direct = DIRECT_RIGHT
        elif self.status == 2:
            # 重新游戏
            if self.key_down[1] == 4:
                self.status = 1
                self.init_game()
            
    def generate_food(self):
        """ 生成食物 """
        while True:
            food = (random.randint(0, 108 // GRID_SIZE - 1) * GRID_SIZE + 10,
                    random.randint(0, 48 // GRID_SIZE - 1) * GRID_SIZE + 16)
            if food not in self.snake.pos:
                self.food = food
                break
        
    def move_handler(self):
        """ 移动蛇 """
        # 生成新的蛇头并插入到头部
        new_head_pos = (self.snake.pos[0][0] + self.snake.direct[0] * GRID_SIZE,
                        self.snake.pos[0][1] + self.snake.direct[1] * GRID_SIZE)
        self.snake.pos.insert(0, new_head_pos)
        
        # 是否吃到食物
        if self.snake.pos[0] == self.food:
            # 加分
            if len(self.snake) < 10:
                self.score += 5
            elif len(self.snake) < 20:
                self.score += 10
            else:
                self.score += 15
            self.generate_food()
            # 提升蛇的移动速度
            self.speed = max(50, self.speed - 5)
            timer_game.init(period=self.speed, mode=Timer.PERIODIC, callback=self.game_loop)
        else:
            # 去除尾部
            self.snake.pos.pop()
            
    def check_over(self):
        """ 游戏结束检测 """
        head_pos = self.snake.pos[0]
        head_x, head_y = head_pos
        # 蛇头碰到边界或蛇身
        if head_x < 10 or head_x >= 118 or head_y < 16 or head_y >= 64 or head_pos in self.snake.pos[1:]:
            self.status = 2
            self.key_down[1] = 0
        
    def update_ui(self):
        """ 更新游戏界面 """
        if self.status == 0:
            # 开始界面
            self.fill_rect(38, 34, 50, 12, 1)
            self.text("Snake!", 40, 36, 0)
        elif self.status == 1:
            # 游戏进行界面
            self.fill_rect(10, 1, 107, 14, 0)
            self.fill_rect(10, 17, 107, 46, 0)
            if self.snake.shape == SNAKE_RECT:
                # 蛇身
                for pos in self.snake.pos:
                    self.fill_rect(pos[0], pos[1], self.snake.size, self.snake.size, 1)
                # 食物
                self.fill_rect(self.food[0], self.food[1], GRID_SIZE, GRID_SIZE, 1)
            else:
                for pos in self.snake.pos:
                    self.fill_circle(pos[0] + 2, pos[1] + 3, GRID_SIZE // 2)
                self.fill_circle(self.food[0] + 2, self.food[1] + 3, GRID_SIZE // 2)
        elif self.status == 2:
            # 游戏结束界面
            self.fill_rect(22, 34, 81, 12, 1)
            self.text("Game Over!", 24, 36, 0)
        self.text(f"SC:{self.score:03d} LN:{len(self.snake):02d}", 14, 4, 1)
        self.show()
        
    def init_ui(self):
        self.rect(9, 0, 109, 16, 1)
        self.rect(9, 16, 109, 48, 1)
        
    def fill_circle(self, x0, y0, radius):
        # 绘制填充圆
        for y in range(-radius, radius + 1):
            for x in range(-radius, radius + 1):
                if x**2 + y**2 <= radius ** 2:
                    self.pixel(x0 + x, y0 + y, 1)
        

if __name__ == "__main__":
    SnakeGame()
    
    

    
